package com.stars.easyms.alarm.aspect;

import com.alibaba.ttl.TransmittableThreadLocal;
import com.stars.easyms.base.annotation.EasyMsAlarm;
import com.stars.easyms.base.interceptor.AbstractEasyMsMethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.util.ConcurrentReferenceHashMap;

import java.lang.reflect.Method;
import java.util.Map;

/**
 * <p>className: EasyMsAlarmAspect</p>
 * <p>description: EasyMs告警注解拦截器:</p>
 * <p> 1.自上往下只要有设置不告警即使下层再次设置需要告警也不会告警
 * 2.
 *
 * @author guoguifang
 * @version 1.7.2
 * @date 2021/1/19 3:14 下午
 */
public class EasyMsAlarmAspect extends AbstractEasyMsMethodInterceptor {

    private static final Map<Method, EasyMsAlarm> METHOD_IS_ALARM_CACHE = new ConcurrentReferenceHashMap<>();

    private static final ThreadLocal<EasyMsAlarm> EASY_MS_ALARM_THREAD_LOCAL = new TransmittableThreadLocal<>();

    @Override
    protected Object intercept(MethodInvocation methodInvocation) throws Throwable {
        // 获取EasyMsAlarm注解，如果没有注解则直接跳过
        Method method = methodInvocation.getMethod();
        EasyMsAlarm easyMsAlarm = getEasyMsAlarmAnnotation(method);
        if (easyMsAlarm == null) {
            return proceed(methodInvocation);
        }

//        !easyMsAlarm.enabled();
        EasyMsAlarm prevEasyMsAlarm = EASY_MS_ALARM_THREAD_LOCAL.get();

        boolean isExceptionAlarm = easyMsAlarm.exception();

        try {
            return proceed(methodInvocation);
        } catch (Throwable throwable) {
            throw throwable;
        }
    }

    /**
     * 获取EasyMsAlarm注解：优先级：当前方法>当前类>父方法>父类
     */
    private EasyMsAlarm getEasyMsAlarmAnnotation(Method method) {
        return METHOD_IS_ALARM_CACHE.computeIfAbsent(method, m -> {
            // 方法的isAnnotationPresent方法不会查找父方法，只招当前方法
            if (m.isAnnotationPresent(EasyMsAlarm.class)) {
                return m.getAnnotation(EasyMsAlarm.class);
            }

            // 如果当前方法没找到则找当前类，类的isAnnotationPresent方法会查找父类，若只招当前类则不使用isAnnotationPresent方法判断
            EasyMsAlarm easyMsAlarm = m.getDeclaringClass().getDeclaredAnnotation(EasyMsAlarm.class);
            if (easyMsAlarm != null) {
                return easyMsAlarm;
            }

            // 获取父方法的注解，使用findMergedAnnotation方法可获得依次往上的第一个有该注解的父方法
            easyMsAlarm = AnnotatedElementUtils.findMergedAnnotation(m, EasyMsAlarm.class);
            if (easyMsAlarm != null) {
                return easyMsAlarm;
            }
            return AnnotatedElementUtils.findMergedAnnotation(m.getDeclaringClass(), EasyMsAlarm.class);
        });
    }

}